//第3章 函数是根基

//-3.1
//--
function isNimble(){ return true; }                            //#1

assert(typeof window.isNimble === "function",                  //#2
    "isNimble() defined");
assert(isNimble.name === "isNimble",
    "isNimble() has a name");

//--
var canFly = function(){ return true; };                       //#3

assert(typeof window.canFly === "function",                    //#4
     "canFly() defined");
console.log( 'canFly.name:'+canFly.name );
assert(canFly.name === "",
     "canFly() has no name");

//--
window.isDeadly = function(){ return true; };                  //#5
assert(typeof window.isDeadly === "function",                  //#6
"isDeadly() defined");

//-- 作用域
function outer(){                                              //#7
assert(typeof inner === "function",
       "inner() in scope before declaration");
function inner(){}
assert(typeof inner === "function",
      "inner() in scope after declaration");
assert(window.inner === undefined,
       "inner() not in global scope");
}

outer();                                                       //#8
assert(window.inner === undefined,
     "inner() still not in global scope");

// 真正被调用时，有效的是这个wieldsSword  ,Function的name属性没什么用
window.wieldsSword = function swingsSword() {

  assert(  typeof swingsSword ,
  "我可以在函数自己的作用域内通过函数的name属性调用自己，尽管该函数在自己的作用域外部不叫这个name");
  return true; };  //#9

assert(window.wieldsSword.name === 'swingsSword',
     "wieldSword's real name is swingsSword");

console.log( wieldsSword() );


//3.2

// 1.javascript中的作用域 取决于 Function定义的边界，这有区别于其他编程语言
// 2.函数定义后在其定义时的作用域内全域有效，
// 3.变量在定义后，从其定义开始到 其定义时的作用域的结束
assert(true,"|----- BEFORE OUTER -----|");          //#1
assert(typeof outer==='function',
    "outer() is in scope");
assert(typeof inner==='function',
    "inner() is in scope");
assert(typeof a==='number',
    "a is in scope");
assert(typeof b==='number',
    "b is in scope");
assert(typeof c==='number',
    "c is in scope");

function outer(){

assert(true,"|----- INSIDE OUTER, BEFORE a -----|");  //#2
assert(typeof outer==='function',
    "outer() is in scope");
assert(typeof inner==='function',
    "inner() is in scope");
assert(typeof a==='number',
    "a is in scope");
assert(typeof b==='number',
    "b is in scope");
assert(typeof c==='number',
    "c is in scope");

var a = 1;

assert(true,"|----- INSIDE OUTER, AFTER a -----|");  //#3
assert(typeof outer==='function',
      "outer() is in scope");
assert(typeof inner==='function',
      "inner() is in scope");
assert(typeof a==='number',
      "a is in scope");
assert(typeof b==='number',
      "b is in scope");
assert(typeof c==='number',
      "c is in scope");

function inner(){ /* does nothing */ }

var b = 2;

assert(true,"|----- INSIDE OUTER, AFTER inner() AND b -----|"); //#4
assert(typeof outer==='function',
      "outer() is in scope");
assert(typeof inner==='function',
      "inner() is in scope");
assert(typeof a==='number',
      "a is in scope");
assert(typeof b==='number',
      "b is in scope");
assert(typeof c==='number',
      "c is in scope");

if (a == 1) {
  var c = 3;
  assert(true,"|----- INSIDE OUTER, INSIDE if -----|"); //#5
  assert(typeof outer==='function',
        "outer() is in scope");
  assert(typeof inner==='function',
        "inner() is in scope");
  assert(typeof a==='number',
        "a is in scope");
  assert(typeof b==='number',
        "b is in scope");
  assert(typeof c==='number',
        "c is in scope");
}

assert(true,"|----- INSIDE OUTER, AFTER c -----|"); //#6
assert(typeof outer==='function',
      "outer() is in scope");
assert(typeof inner==='function',
      "inner() is in scope");
assert(typeof a==='number',
      "a is in scope");
assert(typeof b==='number',
      "b is in scope");
assert(typeof c==='number',
      "c is in scope");

}

outer();

assert(true,"|----- AFTER OUTER -----|"); //#7
assert(typeof outer==='function',
     "outer() is in scope");
assert(typeof inner==='function',
     "inner() is in scope");
assert(typeof a==='number',
     "a is in scope");
assert(typeof b==='number',
     "b is in scope");
assert(typeof c==='number',
     "c is in scope");

//3.3 函数的执行环境 this
//1.函数作为函数调用，在全局作用域下，其执行环境是window
function creep(){ return this; }                     //#1
assert(creep() === window,                           //#1
     "Creeping in the window");                    //#1

var sneak = creep;                                   //#3
assert(sneak() === window,                           //#4
     "Sneaking in the window");                    //#4
//1.函数作为方法调用，其执行环境是调用者对象
var ninja1 = {                                       //#5
skulk: creep                                       //#5
};
assert(ninja1.skulk() === ninja1,                    //#6
     "The 1st ninja is skulking");                 //#6

var ninja2 = {                                       //#7
skulk: creep                                       //#7
};
assert(ninja2.skulk() === ninja2,                    //#8
     "The 2nd ninja is skulking");                 //#8

//3.4
//3.函数作为构造器调用：先创建一个空对象，然后在该空对象上调用构造方法
function Ninja() {                                 //#1
this.skulk = function() { return this; };        //#1
}                                                  //#1

var ninja1 = new Ninja();                          //#2
var ninja2 = new Ninja();                          //#2

assert(ninja1.skulk() === ninja1,                  //#3
     "The 1st ninja is skulking");               //#3
assert(ninja2.skulk() === ninja2,                  //#3
     "The 2nd ninja is skulking");               //#3

//3.5
//apply 和 call ，调用方法时强制修改执行环境
function juggle() {                                 //#1
	var result = 0;
	for (var n = 0; n < arguments.length; n++) {      //#2
	  result += arguments[n];
	}
	this.result = result;                             //#3
}

var ninja1 = {};                                    //#4
var ninja2 = {};                                    //#4

juggle.apply(ninja1,[1,2,3,4]);                     //#5

juggle.call(ninja2,5,6,7,8);                        //#6

assert(ninja1.result === 10,"juggled via apply");   //#7
assert(ninja2.result === 26,"juggled via call");    //#7

//3.6
//函数作为回调函数
function forEach(list,callback) {                   //#1
	for (var n = 0; n < list.length; n++) {
	  callback.call(list[n],n);                       //#2
	}
}

var weapons = ['shuriken','katana','nunchucks']     //#3

forEach(                                             //#4
	weapons,
	function(index){
	  alert( weapons[index] );
	  assert(this == weapons[index],
	         "Got the expected value of " + weapons[index]);
	}
);


// examples of sort
var values = [ 213, 16, 2058, 54, 10, 1965, 57, 9 ];

values.sort(function(value1,value2){ return value2 - value1; });

var ok = true;
for (var n = 0; n < values.length - 1; n++) if (values[n] < values[n+1]) ok = false;
assert(ok,'Array has been reverse sorted.')



//第4章 挥舞函数

//4.1 匿名函数

//4.2 递归
function chirp(n) {                                      //#1
	return n > 1 ? chirp(n - 1) + "-chirp" : "chirp";      //#1
}                                                        //#1

assert(chirp(3) == "chirp-chirp-chirp",                  //#2
    "Calling the named function comes naturally.");    //#2



// arguments.callee指向被调用的函数 (不推荐使用callee)
var ninja = {
chirp: function(n) {                              //#1
  return n > 1 ? arguments.callee(n - 1) + "-chirp" : "chirp";
}
};

assert(ninja.chirp(3) == "chirp-chirp-chirp",      //#2
     "arguments.callee is the function itself.");


// 函数存储
var store = {
	nextId: 1,                                        //#1
	cache: {},                                        //#2
	add: function(fn) {                               //#3
	  if (!fn.id) {                                   //#3
	    fn.id = store.nextId++;                       //#3
	    return !!(store.cache[fn.id] = fn);           //#3
	  }                                               //#3
	}
};

function ninja(){}

assert(store.add(ninja),                            //#4
     "Function was safely added.");               //#4
assert(!store.add(ninja),                           //#4
     "But it was only added once.");              //#4


// 自记忆函数--1
function isPrime(value) {
	if (!isPrime.answers) isPrime.answers = {};                  //#1
	if (isPrime.answers[value] != null) {                        //#2
	  return isPrime.answers[value];                             //#2
	}                                                            //#2
	var prime = value != 1; // 1 can never be prime
	for (var i = 2; i < value; i++) {
			if (value % i == 0) {
			prime = false;
			break;
		}
	}
	return isPrime.answers[value] = prime;                       //#3
}

assert(isPrime(5), "5 is prime!" );                            //#5
assert(isPrime.answers[5], "The answer was cached!" );         //#5

// 伪造数组方法
var elems = {

	length: 0,                                                //#1

	add: function(elem){                                      //#2
		Array.prototype.push.call(this, elem);
	},
	gather: function(id){                                     //#3
		this.add(document.getElementById(id));
	}
};

elems.gather("first");                                      //#4
assert(elems.length == 1 && elems[0].nodeType,              //#4
     "Verify that we have an element in our stash");      //#4
elems.gather("second");                                     //#4
assert(elems.length == 2 && elems[1].nodeType,              //#4
     "Verify the other insertion");                       //#4


// 可变长度的参数列表
function smallest(array){                         //#1
	return Math.min.apply(Math, array);             //#1
}                                                 //#1

function largest(array){                          //#2
	return Math.max.apply(Math, array);             //#2
}                                                 //#2

assert(smallest([0, 1, 2, 3]) == 0,               //#3
     "Located the smallest value.");            //#3
assert(largest([0, 1, 2, 3]) == 3,                //#3
     "Located the largest value.");             //#3

//函数重载
function merge(root){                                   //#1
	for (var i = 1; i < arguments.length; i++) {
	  for (var key in arguments[i]) {
	    root[key] = arguments[i][key];
	  }
	}
	return root;
}

var merged = merge(                                     //#2
	{name: "Batou"},                                      //#2
	{city: "Niihama"}
);                                   //#2

assert(merged.name == "Batou",                          //#3
     "The original name is intact.");                 //#3
assert(merged.city == "Niihama",                        //#3
     "And the city has been copied over.");           //#3


//      
function multiMax(multi){
	//这会有问题哦 ,arguments只是有类似数组的特性，但是并不是数组，所以不能直接调用slice方法
	return multi * Math.max.apply(Math, arguments.slice(1)); 
}
assert(multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by largest.)");

//
function multiMax(multi){
	return multi * Math.max.apply(Math,
	  Array.prototype.slice.call(arguments, 1));           //#1
	  //欺骗 Array将 arguments当做一个数组来对待
}

assert(multiMax(3, 1, 2, 3) == 9,
     "3*3=9 (First arg, by largest.)");

// --------------

// 函数重载 示例
var ninjas = {                                                    //#1
	values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};

function addMethod(object, name, fn) {
	var old = object[name]; 
	// 这里使用到了闭包                       //#1
	object[name] = function(){
		if (fn.length == arguments.length)           //#2
		  return fn.apply(this, arguments)           //#2
		else if (typeof old == 'function')           //#3
		  return old.apply(this, arguments);         //#3
	};
}
addMethod(ninjas, "find", function(){                             //#2
	return this.values;
});

addMethod(ninjas, "find", function(name){                         //#3
	var ret = [];
	for (var i = 0; i < this.values.length; i++)
	  if (this.values[i].indexOf(name) == 0)
	    ret.push(this.values[i]);
	return ret;
});

addMethod(ninjas, "find", function(first, last){                 //#4
	var ret = [];
	for (var i = 0; i < this.values.length; i++)
	  if (this.values[i] == (first + " " + last))
	    ret.push(this.values[i]);
	return ret;
});

assert(ninjas.find().length == 3,                                //#5
     "Found all ninjas");
assert(ninjas.find("Sam").length == 1,
     "Found ninja by first name");
assert(ninjas.find("Dean", "Edwards").length == 1,
     "Found ninja by first and last name");
assert(ninjas.find("Alex", "Russell", "Jr") == null,
     "Found nothing");


// 第5章 闭包
	//简单地说，闭包(closure)是一个函数在创建时允许该自身函数访问并操作该自身函数之外的
	//变量时说创建的作用域。
	//换句话，闭包可以让函数访问所有的变量和函数，只要这些变量和函数
	//存在于该函数声明时的作用域内就行。

var outerValue = 'ninja';
var later;                                                   //#1
function outerFunction() {
	var innerValue = 'samurai';                                //#2
	function innerFunction() {                                 //#3
		assert(outerValue,"I can see the ninja.");               //#3
		assert(innerValue,"I can see the samurai.");             //#3
	}                                                          //#3
	later = innerFunction;                                     //#4
}
outerFunction();                                             //#5
later();                                                     //#6


//使用闭包 之 私有变量
function Ninja() {                                            //#1
	var feints = 0;                                             //#2
	this.getFeints = function(){                                //#3
		return feints;                                            //#3
	};                                                          //#3
	this.feint = function(){                                    //#4
		feints++;                                                 //#4
	};                                                          //#4
}
var ninja = new Ninja();                                      //#5
ninja.feint();                                                //#6
assert(ninja.feints === undefined,                            //#7
  "And the private data is inaccessible to us." );          //#7
assert(ninja.getFeints() == 1,                                //#8
     "We're able to access the internal feint count." );    //#8



//使用闭包 之 回调(callback)

jQuery('#testButton').click(function(){                          //#1
	var elem$ = jQuery("#testSubject");                            //#2
	elem$.html("Loading...");                                      //#3
	jQuery.ajax({
		url: "test.html",
		success: function(html){                                     //#4
			assert(elem$,
			      "We can see elem$, via the closure for this callback.");
			elem$.html(html);
		}
	});
});

// 使用闭包 之 计时器
function animateIt(elementId) {
	var elem = document.getElementById(elementId);              //#2
	var tick = 0;                                               //#3

	var timer = setInterval(function(){                         //#4
		if (tick < 100) {
			elem.style.left = elem.style.top = tick + "px";
			tick++;
		}
		else {
			clearInterval(timer);
			assert(tick == 100,                                      //#5
			"Tick accessed via a closure.");
			assert(elem,
			"Element also accessed via a closure.");
			assert(timer,
			"Timer reference also obtained via a closure." );
		}
	}, 10);

}

animateIt('box');    

//  绑定函数上下文（执行环境 this）
function bind(context,name){                                 //#1
	return function(){                                         //#1
	  return context[name].apply(context,arguments);           //#1
	};                                                         //#1
}                                                            //#1

var button = {
	clicked: false,
	click: function(){
		this.clicked = true;
		assert(button.clicked,"The button has been clicked");
		console.log(this);
	}
};

var elem = document.getElementById("test");
elem.addEventListener("click",bind(button,"click"),false);     //#2

//  绑定函数上下文 （初版prototye实现）
Function.prototype.bind = function(){
	// fn 引用的是 调用的Function本身                                     //#1
	var fn = this, args = Array.prototype.slice.call(arguments),  //这里的arguments指代调用bind时的传入参数
	// object 是 实际要调用的对象，他是在bind方法的第一个参数
	object = args.shift();
	return function(){
		// 返回一个 绑定了对象的 函数
		return fn.apply(object,
		  args.concat(Array.prototype.slice.call(arguments)));  //这里的arguments指代调用myFunction时的传入参数
	};
};

var myObject = {};
function myFunction(){
  return this == myObject;
}
assert( !myFunction(), "Context is not set yet" );
var aFunction = myFunction.bind(myObject)
assert( aFunction(), "Context is set properly" );


// 偏应用函数
// 5.11 柯里化函数示例
Function.prototype.partial = function() {
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function() {
    var arg = 0;
    for (var i = 0; i < args.length && arg < arguments.length; i++) {
      if (args[i] === undefined) {
        args[i] = arguments[arg++];
      }
    }
    return fn.apply(this, args);
  };
};

String.prototype.csv = String.prototype.split.partial(/,\s*/);  //#1
var results = ("Mugan, Jin, Fuu").csv();                        //#2
assert(results[0]=="Mugan" &&                                   //#3
     results[1]=="Jin" &&                                     //#3
     results[2]=="Fuu",                                       //#3
     "The text values were split properly");                  //#3
 //  最后执行的是  String.prototype.split.apply( "Mugan, Jin, Fuu" , /,\s*/ )  ;

// ------------------
// “分部应用”，在函数调用之前，我们可以预想传入一些函数。实际上，偏应用函数返回了一个
// 含预处理参数的新函数，以便后期可以调用
// 这类代理函数---代理的是另一函数，并且在执行的时候回调用所代理的函数。
// 这种在一个函数中首先填充几个参数（然后返回一个新函数）的技术称之为currying


//5.12
Function.prototype.partial = function() {
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function() {
    var arg = 0;
    for (var i = 0; i < args.length && arg < arguments.length; i++) {
      if (args[i] === undefined) {
        args[i] = arguments[arg++];
      }
    }
    return fn.apply(this, args);
  };
};

Math.maxAbove500 = Math.max.partial(500);

assert(Math.maxAbove500(3,4,1,6) == 500, "Max of 500, 3, 4, 1, 6 is 500"); //#4
assert(Math.maxAbove500(3,4,11223,1,6) == 11223, "Max of 500, 3, 4, 11223, 1, 6 is 11223");      //#4


//5.13  普通方法 实现 缓存记忆，需要特意调用方法
Function.prototype.memoized = function(key){
	this._values = this._values || {};                            //#1
	return this._values[key] !== undefined ?                      //#2
	  this._values[key] :
	  this._values[key] = this.apply(this, arguments);
};

function isPrime(num) {                                         //#3
	var prime = num != 1;
	for (var i = 2; i < num; i++) {
	  if (num % i == 0) {
	    prime = false;
	    break;
	  }
	}
	return prime;
}
assert(isPrime.memoized(5), "The function works; 5 is prime."); //#4
assert(isPrime._values[5], "The answer has been cached.");      //#4

// 5.14 
// 不变
Function.prototype.memoized = function(key){
	//调用memoized时，传入的上下文件 this 是 isPrime函数
	this._values = this._values || {};
	return this._values[key] !== undefined ?
	  this._values[key] :
	  this._values[key] = this.apply(this, arguments);
};

// 包装功能函数，返回一个 被包装后的函数
Function.prototype.memoize = function(){
	var fn = this;  //记录功能方法为fn                     //#1
	return function(){                              //#2
	  return fn.memoized.apply(fn, arguments); // 在fn上下文上调用 fn.memoized的函数
	  //这样，用户不必知道也特地去调用 fn.memoized方法就能缓存计算结果
	};
};

// 功能函数 
var isPrime = (function(num) {
	var prime = num != 1;
	for (var i = 2; i < num; i++) {
	  if (num % i == 0) {
	    prime = false;
	    break;
	  }
	}
	return prime;
}).memoize();
assert(isPrime(17),"17 is prime");                //#3
// 但是 5.14这种 包装实现， isPrime作为原始方法，
// 在流程中，不能因条件的不同调用原始方法还是调用包装后的方法


// 5.15 Prototype框架中   Element.readAttribute(elem,attr) 的包装方法的实现
function wrap(object, method, wrapper) {                        //#1
  var fn = object[method];                                      //#2
  return object[method] = function() {       
     //要将传递给 wrapper(original,elem,attr) 的original的上下文设置为object，否则上下文会变成window
    return wrapper.apply(this, [fn.bind(this)].concat( 
        Array.prototype.slice.call(arguments)));
  };
}
// Example adapted from Prototype
if (Prototype.Browser.Opera) {                                 //#4
  wrap(Element.Methods, "readAttribute",                       //#5
       function(original, elem, attr) {
    return attr == "title" ?
        elem.title :
        original(elem, attr);
  });

}

//新版 prototype 方法
 function wrap(wrapper) {
    var __method = this;
    return function() {
      var a = update([__method.bind(this)], arguments);
      return wrapper.apply(this, a);
    }
  }

//5.16
$ = function(){ alert('not jQuery!'); };             //#1

(function($){                                        //#2

$('img').on('click',function(event){               //#3
  $(event.target).addClass('clickedOn');
})

})(jQuery);                                          //#4





// 即时函数， 临时作用域和私有变量的使用
document.addEventListener( "click",
		(function(){
			var numClicks = 0;
			return function(){
				alert( ++ numClicks);
			}
		})()
,false );


// 5.17  5.18
var divs = document.getElementsByTagName("div");  //#1

	for (var i = 0; i < divs.length; i++) {
		

		divs[i].addEventListener("click", function() {
			// 闭包保存的是变量的引用 i,当 div真正被click的时候，i的值早已是divs.length的最后一个值了。
	  		alert("divs #" + i + " was clicked.");        //#2
		}, false);
	}
// --
var div = document.getElementsByTagName("div");

for (var i = 0; i < div.length; i++) 
	// 执行一个 即时函数， 传入的 n 是 形式参数，会在闭包里创建一个新的变量 n，保存的是传入时i的值。
(
function(n) {
	div[i].addEventListener("click", function() {
	  alert("div #" + n + " was clicked.");
	}, false);
}
)(i);



// 第6章 原型与面向对象

// 原型是 实时附加在对象上的
// 对象自己的属性 优先于 原型的同名属性

// 6.5 使用 instanceof 判断继承关系 ，constructor 判断对象的构造函数
// 

//6.9 让 低级浏览器的 Array 支持forEach方法
if (!Array.prototype.forEach) {                                //#1
Array.prototype.forEach = function(callback, context) {      //#2
  for (var i = 0; i < this.length; i++) {
    callback.call(context || null, this[i], i, this);        //#3
  }
};
}

["a", "b", "c"].forEach(function(value, index, array) {        //#4
assert(value,  "Is in position " + index + " out of " +
       (array.length - 1));
});

//6.10
// 让低版本 浏览器的  HTMLElement 支持 reomve 自己的方法
HTMLElement.prototype.remove = function() {     //#1
if (this.parentNode)
  this.parentNode.removeChild(this);
};

var a = document.getElementById("a");            //#2
a.parentNode.removeChild(a);                     //#2

document.getElementById("b").remove();           //#3

assert(!document.getElementById("a"),"a is gone.");
assert(!document.getElementById("b"),"b is gone too.");


// for (var p in obj) 会遍历 包括 原型在内的属性，所以，最好不会随便给 基础对象Object对象添加属性

// 6.12 hasOwnProperty的使用
Object.prototype.keys = function() {
var keys = [];
for (var i in this)
  if (this.hasOwnProperty(i))                       //#1
    keys.push(i);
return keys;
};

var obj = { a: 1, b: 2, c: 3 };

assert(obj.keys().length == 3,                        //#2
  "There are three properties in this object.");

// 当将 new Array()作为 自己的一个对象的原型的时候，在IE低版本中length会失效，
// 以下方式实现 数组的功能的实现
 function MyArray() {}                                         //#1
MyArray.prototype.length = 0;                                 //#1

(function() {                                                 //#2
var methods = ['push', 'pop', 'shift', 'unshift',
  'slice', 'splice', 'join'];

for (var i = 0; i < methods.length; i++)
(function(name) {
  MyArray.prototype[ name ] = function() {
    return Array.prototype[ name ].apply(this, arguments);
  };
})(methods[i]);

})();

var mine = new MyArray();                                     //#3
mine.push(1, 2, 3);
assert(mine.length == 3,
     "All the items are on our sub-classed array.");
assert(!(mine instanceof Array),
     "We aren't subclassing Array, though.");

//6.19 arguments.callee 实现 函数作为 构造函数才被调用，否则，我们手动给他调用构造函数
function User(first, last) {
if (!(this instanceof arguments.callee)) {             //#1
  return new User(first,last);                         //#1
}                                                      //#1
this.name = first + " " + last;
}

var name = "Rukia";

var user = User("Ichigo", "Kurosaki");                    //#2

assert(name == "Rukia","Name was set to Rukia.");         //#3
assert(user instanceof User, "User instantiated");        //#3
assert(user.name == "Ichigo Kurosaki",                    //#3
     "User name correctly assigned");                   //#3


// 通用 继承实现
(function() {
  var initializing = false,
      superPattern =  // Determine if functions can be serialized
        /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;       //#1

  // Creates a new Class that inherits from this class
  Object.subClass = function(properties) {                           //#2
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;                                              //#3
    var proto = new this();                                           //#3
    initializing = false;                                             //#3

    // Copy the properties over onto the new prototype
    for (var name in properties) {                                    //#4
      // Check if we're overwriting an existing function
      proto[name] = typeof properties[name] == "function" &&
                    typeof _super[name] == "function" &&
                    superPattern.test(properties[name]) ?
          (function(name, fn) {                                        //#5
            return function() {
              var tmp = this._super;

              // Add a new ._super() method that is the same method
              // but on the super-class
              this._super = _super[name];

              // The method only need to be bound temporarily, so we
              // remove it when we're done executing
              var ret = fn.apply(this, arguments);
              this._super = tmp;

              return ret;
            };
          })(name, properties[name]) :
          properties[name];
    }

    // The dummy class constructor
    function Class() {                                                   //#6
      // All construction is actually done in the init method
      if (!initializing && this.init)
        this.init.apply(this, arguments);
    }

    // Populate our constructed prototype object
    Class.prototype = proto;                                             //#7

    // Enforce the constructor to be what we expect
    Class.constructor = Class;                                           //#8

    // And make this class extendable
    Class.subClass = arguments.callee;                                   //#9

    return Class;
  };
})();

// 通用继承实现测试代码

      var Person = Object.subClass({                           //#1
        init: function(isDancing) {
          this.dancing = isDancing;
        },
        dance: function() {
          return this.dancing;
        }
      });

      var Ninja = Person.subClass({                            //#2
        init: function() {
          this._super(false);                                  //#3
        },
        dance: function() {
          // Ninja-specific stuff here
          return this._super();
        },
        swingSword: function() {
          return true;
        }
      });

      var person = new Person(true);                            //#4
      assert(person.dance(),                                    //#4
             "The person is dancing.");                         //#4

      var ninja = new Ninja();                                  //#5
      assert(ninja.swingSword(),                                //#5
             "The sword is swinging.");                         //#5
      assert(!ninja.dance(),                                    //#5
             "The ninja is not dancing.");                      //#5

      assert(person instanceof Person,                          //#6
             "Person is a Person.");                            //#6
      assert(ninja instanceof Ninja &&                          //#6
             ninja instanceof Person,                           //#6
             "Ninja is a Ninja and a Person.");                 //#6

// 第7章 正则表达式


//7.2
var re1 = /test/i;                           //#1

var re2 = new RegExp("test", "i");           //#2

assert(re1.toString() == "/test/i",
     "Verify the contents of the expression.");
assert(re1.test("TesT"), "Yes, it's case-insensitive.");
assert(re2.test("TesT"), "This one is too.");
assert(re1.toString() == re2.toString(),
    "The regular expressions expressions are equal.");
assert( re1 != re2, "But they are different objects.");


// 7.3 findClassInElements
function findClassInElements(className, type) {

var elems =                                            //#2
  document.getElementsByTagName(type || "*");

var regex =                                            //#3
  new RegExp("(^|\\s)" + className + "(\\s|$)");

var results = [];                                      //#4

for (var i = 0, length = elems.length; i < length; i++)
  if (regex.test(elems[i].className)) {                //#5
    results.push(elems[i]);
  }
return results;
}

assert(findClassInElements("ninja", "div").length == 2,
     "The right amount fo div ninjas was found.");
assert(findClassInElements("ninja", "span").length == 1,
     "The right amount of span ninjas was found.");
assert(findClassInElements("ninja").length == 3,
     "The right amount of ninjas was found.");

// 7.4  match
//如果 regexp 没有标志 g，那么 match() 方法就只能在 stringObject 中执行一次匹配。如果没有找到任何匹配的文本， match() 将返回 null。否则，它将返回一个数组，其中存放了与它找到的匹配文本有关的信息。该数组的第 0 个元素存放的是匹配文本，而其余的元素存放的是与正则表达式的子表达式匹配的文本。除了这些常规的数组元素之外，返回的数组还含有两个对象


function getOpacity(elem) {
var filter = elem.style.filter;
return filter ?                                      //#2
filter.indexOf("opacity=") >= 0 ?
    (parseFloat(filter.match(/opacity=([^)]+)/)[1]) / 100) + "" :
    "" :
elem.style.opacity;
}

window.onload = function() {
assert(
    getOpacity(document.getElementById("opacity")) == "0.5",
    "The opacity of the element has been obtained.");
};


// 7.5 match /g
// 如果 regexp 具有标志 g，则 match() 方法将执行全局检索，找到 stringObject 中的所有匹配子字符串。若没有找到任何匹配的子串，则返回 null。如果找到了一个或多个匹配子串，则返回一个数组。不过全局匹配返回的数组的内容与前者大不相同，它的数组元素中存放的是 stringObject 中所有的匹配子串，而且也没有 index 属性或 input 属性。
// 
var html = "<div class='test'><b>Hello</b> <i>world!</i></div>";
var pattern = /<(\/?)(\w+)([^>]*?)>/g, match;
var num = 0;

while ((match = pattern.exec(html)) !== null) {                  //#1
assert(match.length == 4,
      "Every match finds each tag and 3 captures.");
num++;
}
assert(num == 6, "3 opening and 3 closing tags found.");

// 7.6 exec
var html = "<b class='hello'>Hello</b> <i>world!</i>";

var pattern = /<(\w+)([^>]*)>(.*?)<\/\1>/g;              //#1

var match = pattern.exec(html);
console.log(match);

assert(match[0] == "<b class='hello'>Hello</b>",
     "The entire tag, start to finish.");
assert(match[1] == "b", "The tag name.");
assert(match[2] == " class='hello'", "The tag attributes.");
assert(match[3] == "Hello", "The contents of the tag.");

match = pattern.exec(html);

console.log(match);

assert(match[0] == "<i>world!</i>",
     "The entire tag, start to finish.");
assert(match[1] == "i", "The tag name.");
assert(match[2] == "", "The tag attributes.");
assert(match[3] == "world!", "The contents of the tag.");

// String.replace
function upper(all,letter)
{ return letter.toUpperCase(); }      //#1

assert("border-bottom-width".replace(/-(\w)/g,upper)             //#2
      == "borderBottomWidth",
  "Camel cased a hyphenated string.");


//7.10 compress
function compress(source) {
var keys = {};                                     //#1

source.replace(
  /([^=&]+)=([^&]*)/g,
  function(full, key, value) {                     //#2
    keys[key] =
      (keys[key] ? keys[key] + "," : "") + value;
    return "";
  }
);

var result = [];                                   //#3
for (var key in keys) {                            //#3
  result.push(key + "=" + keys[key]);              //#3
}                                                  //#3

return result.join("&");                           //#4
}

assert(compress("foo=1&foo=2&blah=a&blah=b&foo=3") ==
     "foo=1,2,3&blah=a,b",
     "Compression is OK!");


// String. trim()
function trim(str) {
return (str||"").replace(/^\s+|\s+$/g, "");          //#1
}

assert(trim(" #id div.class ") == "#id div.class",
     "Extra whitespace trimmed from a selector string.");

function trim(str) {
return str.replace(/^\s\s*/, '')           //#1
           .replace(/\s\s*$/, '');         //#1
}

assert(trim(" #id div.class ") == "#id div.class",
     "Extra whitespace trimmed from a selector string.");

function trim(str) {                          //#1
var str = str.replace(/^\s\s*/, ''),
    ws = /\s/,
    i = str.length;
while (ws.test(str.charAt(--i)));
return str.slice(0, i + 1);
}

assert(trim(" #id div.class ") == "#id div.class",
  "Extra whitespace trimmed from a selector string.");



// 正则匹配 换行符 \n
var html = "<b>Hello</b>\n<i>world!</i>";                   //#1

assert(/.*/.exec(html)[0] === "<b>Hello</b>",               //#2
     "A normal capture doesn't handle endlines.");

assert(/[\S\s]*/.exec(html)[0] ===                          //#3
     "<b>Hello</b>\n<i>world!</i>",
     "Matching everything with a character set.");

assert(/(?:.|\s)*/.exec(html)[0] ===                        //#4
     "<b>Hello</b>\n<i>world!</i>",
     "Using a non-capturing group to match everything.");



// 第8章  定时器和线程
// 8.1
setTimeout(function repeatMe() {                //#1
/* Some long block of code... */              //#1
setTimeout(repeatMe, 10);                     //#1
}, 10);                                         //#1

setInterval(function() {                        //#2
/* Some long block of code... */              //#2
}, 10);                                         //#2

// 定时器 实现 大计算量时的 不阻塞 设计
var rowCount = 20000;                                       //#1
var divideInto = 4;                                         //#1
var chunkSize = rowCount/divideInto;                        //#1
var iteration = 0;                                          //#1

var table = document.getElementsByTagName("tbody")[0];

setTimeout(function generateRows(){
var base = chunkSize * iteration;                         //#2
for (var i = 0; i < chunkSize; i++) {
  var tr = document.createElement("tr");
  for (var t = 0; t < 6; t++) {
    var td = document.createElement("td");
    td.appendChild(document.createTextNode((i + base) + "," + t + "," + iteration));
    tr.appendChild(td);
  }
  table.appendChild(tr);
}
iteration++;                                              //#3
if (iteration < divideInto)                               //#3
  setTimeout(generateRows,0);  // 将方法压入 队列，不一定会马上执行，浏览器可能会先渲染页面再执行
},0);


// 中央 定时器，实现 顺序执行方法
var timers = {                                                  //#1

timerID: 0,                                                   //#2
timers: [],                                                   //#2

add: function(fn) {                                           //#3
  this.timers.push(fn);
},

start: function runNext() {                                   //#4
  if (this.timerID) return;
  (function() {
    if (timers.timers.length > 0) {
      for (var i = 0; i < timers.timers.length; i++) {
        if (timers.timers[i]() === false) {
          timers.timers.splice(i,1);
          i--;
        }
      }
      timers.timerID = setTimeout(runNext, 0);
    }
  })();
},

stop: function() {                                             //#5
  clearTimeout(this.timerID);
  this.timerID = 0;
}

};

var box = document.getElementById("box"), x = 0, y = 20;

timers.add(function() {
box.style.left = x + "px";
if (++x > 50) return false;
});

timers.add(function() {
box.style.top = y + "px";
y += 2;
if (y > 120) return false;
});

timers.start();



// 8.5  异步测试
(function() {

	var queue = [], paused = false;                //#1

	this.test = function(fn) {                     //#2 this is window
	  queue.push(fn);
	  runTest();
	};

	this.pause = function() {                      //#3
	  paused = true;
	};

	this.resume = function() {                     //#4
	  paused = false;
	  setTimeout(runTest, 1);
	};

	function runTest() {                           //#5
	  if (!paused && queue.length) {
	    queue.shift()();
	    if (!paused) resume();
	  }
	}
	})();

	test(function() {
	pause();
	setTimeout(function() {
	  assert(true, "First test completed");
	  resume();
	}, 100);
	});

	// 还是 fun1 先执行， fun2后执行
	test(function() {
	pause();
	setTimeout(function() {
	  assert(true, "Second test completed");
	  resume();
	}, 50);


});